using System;
using System.IO;
using System.Collections.Generic;
using System.Text;
using System.Reflection;



namespace Open.Data.Persistence.SqlServer
{
    //Common useful functions for the SqlServer provider
    static internal class SqlCommon
    {
        public static string SqlFromParameter(IParameter parameter)
        {
            string sql = SqlFromType(parameter.Type);

            //String lengths
            if (parameter is StringParameter)
            {
                StringParameter stringParm = parameter as StringParameter;

                if (stringParm.Length > 0)
                {
                    sql += "(";
                    sql += stringParm.Length.ToString();
                    sql += ")";
                    return sql;
                }
                sql += "(MAX)";
            }

            //Exact precision and scale
            if (parameter is DecimalParameter )
            {
                DecimalParameter decimalParm = parameter as DecimalParameter;

                sql += "(";
                sql += decimalParm.Precision.ToString();
                sql += ",";
                sql += decimalParm.Scale.ToString();
                sql += ")";
                return sql;
            }

            return sql;
        }

        //Return the sql type and precision from a property
        public static string SqlFromProperty(PropertyInfo prop)
        {
            string sql = SqlFromType(prop.PropertyType);

            //Set varchar length
            if (prop.PropertyType == typeof(string))
            {
                object[] attributes = prop.GetCustomAttributes(false);

                foreach (Attribute attr in attributes)
                {
                    if (attr is SqlLengthAttribute)
                    {
                        SqlLengthAttribute max = attr as SqlLengthAttribute;
                        if (max.Length > 0)
                        {
                            sql += "(";
                            sql += max.Length.ToString();
                            sql += ")";
                            return sql;
                        }
                    }
                }
                sql += "(MAX)";
            }

            //Check for fixed attribute
            if (prop.PropertyType == typeof(decimal))
            {
                object[] attributes = prop.GetCustomAttributes(false);

                foreach (Attribute attr in attributes)
                {
                    if (attr is SqlFixedAttribute)
                    {
                        SqlFixedAttribute fix = attr as SqlFixedAttribute;
                        sql += "(";
                        sql += fix.Precision.ToString();
                        sql += ",";
                        sql += fix.Scale.ToString();
                        sql += ")";
                        return sql;
                    }
                }

            }

            return sql;
        }

        //Return the sql data type
        public static string SqlFromType(Type type)
        {
            if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>))
            {
                return SqlFromType(type.GetGenericArguments()[0]);
            }

            if (type == typeof(string))
            {
                return "NVARCHAR";
            }
            else if (type == typeof(long))
            {
                return "BIGINT";
            }
            else if (type == typeof(int))
            {
                return "INT";
            }
            else if (type == typeof(DateTime))
            {
                return "DATETIME";
            }
            if (type == typeof(bool))
            {
                return "BIT";
            }
            else if (type == typeof(byte))
            {
                return "TINYINT";
            }
            else if (type == typeof(short))
            {
                return "SMALLINT";
            }
            else if (type == typeof(double))
            {
                return "FLOAT";
            }
            else if (type == typeof(float))
            {
                return "REAL";
            }
            else if (type == typeof(decimal))
            {
                return "DECIMAL";
            }
            else if (type == typeof(char))
            {
                return "NCHAR";
            }
            else if (type == typeof(System.Guid))
            {
                return "UNIQUEIDENTIFIER";
            }
            else if (typeof(Enum).IsAssignableFrom(type))
            {
                return "SMALLINT";
            }
            else if (typeof(IPersistable).IsAssignableFrom(type))
            {
                CacheItem cache = SqlProvider.Instance.ReflectionCache.Lookup(type);
                return SqlFromProperty(cache.PrimaryKey);
            }
            else if (typeof(Stream).IsAssignableFrom(type))
            {
                return "VARBINARY(MAX)";
            }

            throw new DeploymentException("A matching sql type was not found for " + type.ToString());
        }

        public static string SqlFromJoin(JoinType joinType)
        {
            if (joinType == JoinType.Inner)
            {
                return "";
            }
            else if (joinType == JoinType.Left)
            {
                return "LEFT ";
            }
            else if (joinType == JoinType.Right)
            {
                return "RIGHT ";
            }
            else if (joinType == JoinType.Outer)
            {
                return "OUTER ";
            }
            else return "CROSS ";
        }

        //Return the sql data type
        public static string DefaultFromType(Type type)
        {
            if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>))
            {
                return DefaultFromType(type.GetGenericArguments()[0]);
            }

            else if (type == typeof(long))
            {
                return "(0)";
            }
            else if (type == typeof(int))
            {
                return "(0)";
            }
            else if (type == typeof(DateTime))
            {
                return "('01 Jan 1901')";
            }
            if (type == typeof(bool))
            {
                return "(0)";
            }
            else if (type == typeof(byte))
            {
                return "(0)";
            }
            else if (type == typeof(short))
            {
                return "(0)";
            }
            else if (type == typeof(double))
            {
                return "(0)";
            }
            else if (type == typeof(float))
            {
                return "(0)";
            }
            else if (type == typeof(decimal))
            {
                return "(0)";
            }
            else if (type == typeof(System.Guid))
            {
                return "NEWID()";
            }
            else if (typeof(Enum).IsAssignableFrom(type))
            {
                return "(0)";
            }

            throw new DeploymentException("A default value was not found for type " + type.ToString());
        }

        //Count aggregate type removed for version 2.0
        //As there is no guarantee the target field will be numeric
        public static string GetAggregate(AggregateType aggregateType)
        {
            if (aggregateType == AggregateType.Avg) return "AVG";
            if (aggregateType == AggregateType.Max) return "MAX";
            if (aggregateType == AggregateType.Min) return "MIN";
            if (aggregateType == AggregateType.Sum) return "SUM";

            throw new PersistenceException("A value was not found for aggregate type " + aggregateType.ToString());
        }

        public static void AppendPrimaryKeyNames(StringBuilder builder, Type type, CacheItem cache)
        {
            if (cache.HasPrimaryKey)
            {
                builder.Append("([");
                builder.Append(SqlFormatter.GetPropertyName(cache.PrimaryKey, "."));
                builder.Append("])");
            }
            else if (cache.HasCompoundKeys)
            {
                builder.Append("([");

                bool flag = false;
                foreach (PropertyInfo prop in cache.CompoundKeys)
                {
                    if (flag) builder.Append("],[");
                    builder.Append(SqlFormatter.GetPropertyName(prop, "."));
                    flag = true;
                }

                builder.Append("])");
            }
            else
            {
                throw new PersistenceException("Primary or compound key not found for Type " + type.FullName.ToString() + ".");
            }
        }
    }
}
